﻿using System.Collections.Generic;
using System.Xml;
using System.Data;
using SdmxMl.Helper;
using SdmxMl.Manager;
using SdmxMl.Common;

namespace SdmxMl.Structure
{
    /// <summary>
    /// The CodelistRefType provides the structure for a codelist reference. 
    /// (Note that this is structured differently than a similarly-named type in the 
    /// Registry namespace.) At a minimum, either: AgencyID has the ID of an agency as a 
    /// value; CodelistID takes the ID of a codelist maintained by that agency; and Version 
    /// specifies the version of the codelist; or URN supplies a valid SDMX Registry URN 
    /// (see the SDMX Registry Specification). Alias is used to carry the identifier for the 
    /// referenced codelist, so that codes from that list can be easily referenced by the CodeRefs 
    /// contained in the parent Hierarchy, without having to repeat the agency and version for 
    /// each reference. The Alias must be unique within the parent Hierarchical Codelist.
    /// </summary>
    public class CodelistRefType : BaseSetlistRef
    {
 
        #region Properties

        private bool _isHierarchyref;
        public bool IsHierarchyRef
        {
            get { return _isHierarchyref; }
            set 
            { _isHierarchyref = value;
                if (_isHierarchyref)
                    _idTag = "HierarchicalCodelistID";
            }
        }

        private string _alias;
        public virtual string Alias
        {
            get { return _alias; }
            set { _alias = value; }
        }

        #endregion //Properties

        #region Constructors

        public CodelistRefType() 
        {
        }

        public CodelistRefType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr, string tag, bool getInner)
            
        {
            // CodeList or Hierarchy
            XmlNode ndRef = getInner ? nodeArtefact.SelectSingleNode(tag, nsmgr) : nodeArtefact;
            if (ndRef != null)
            {
                ReadXml(ndRef, nsmgr, "CodelistID");
            }
            else
            {
                ndRef = nodeArtefact.SelectSingleNode(tag, nsmgr);
                if (ndRef != null)
                {
                    IsHierarchyRef = true;
                    ReadXml(ndRef, nsmgr, "HierarchicalCodelistID");
                }
            }


            if (nsmgr.VersionSdmx == SdmxVersion.SDMX_20)
                Alias = Xmlhelper.GetNodeText(nodeArtefact, "./structure:Alias", nsmgr);
            else
                Alias = Xmlhelper.GetAtt(nodeArtefact, "alias");
        }

        #endregion // Constructors

        #region XML Representation
        
        public override void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_21)
                WriteSdmx_2_1(xw, prefix);
            else // SDMX 2.0
                WriteSdmx_2_0(xw, prefix, tag);
        }

        private void WriteSdmx_2_1(XmlTextWriter xw, string prefix)
        {
            xw.WriteStartElement(prefix, "IncludedCodelist", null);
            xw.WriteAttributeString("alias", _alias);

            xw.WriteStartElement("Ref");
            xw.WriteAttributeString("agencyID", AgencyId);
            xw.WriteAttributeString("id", Id);
            if (Version != "1.0")
                xw.WriteAttributeString("version", Version);
            xw.WriteEndElement();

            xw.WriteEndElement(); 
        }

        private void WriteSdmx_2_0(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            Xmlhelper.WriteElementIfExist(xw, prefix, "URN", Urn);
            Xmlhelper.WriteElementIfExist(xw, prefix, "AgencyID", AgencyId);
            if (IsHierarchyRef)
            {
                Xmlhelper.WriteElementIfExist(xw, prefix, "HierarchicalCodelistID", Id);
                if (Version != "1.0")
                    Xmlhelper.WriteElementIfExist(xw, prefix, "Version", Version);
            }
            else
            {
                Xmlhelper.WriteElementIfExist(xw, prefix, "CodelistID", Id);
                if (Version != "1.0")
                    Xmlhelper.WriteElementIfExist(xw, prefix, "Version", Version);
                Xmlhelper.WriteElementIfExist(xw, prefix, "Alias", _alias);
            }
            xw.WriteEndElement();
        }
 
        #endregion // XML Representation

        #region String Representation

        public override string ToString()
        {
            return Alias + " - " + Id;
        }
        #endregion
    }

    public class CodelistRefTypeList : List<CodelistRefType>
    {
        #region ObjectList Representation

        public List<object> GetRowObjects(CodelistRefType crt)
        {
            CodeListType clType = null;
            List<object> lo = new List<object>(5);

            clType = ReferenceManager.LocateCodelist(crt);
            string codelistName = clType == null ? string.Empty : clType.Version + " - " + clType.Name.ToString();
            lo.Add(clType != null);
            lo.Add(crt.Alias);
            lo.Add(crt.Id);
            lo.Add(codelistName);
            lo.Add(crt.Version);

            return lo;
        }
        #endregion

        #region Table representation

        protected DataTable DefineDataTableRepresentation()
        {
            DataTable dataTable = new DataTable();
            dataTable.Columns.Add(new DataColumn("Ref", typeof(bool)));
            dataTable.Columns.Add(new DataColumn("Alias", typeof(string)));
            dataTable.Columns.Add(new DataColumn("Codelist", typeof(string)));
            dataTable.Columns.Add(new DataColumn("Name", typeof(string)));
            dataTable.Columns.Add(new DataColumn("Version", typeof(string)));
            return dataTable;
        }

        public DataTable GetTableRepresentation()
        {
            DataTable dataTable = DefineDataTableRepresentation();

            foreach (CodelistRefType crt in this)
                dataTable.Rows.Add(GetRowObjects(crt).ToArray());

            return dataTable;
        }
        #endregion

        #region Dictionary representation

        public Dictionary<string, CodeListType> GetRefDictionary()
        {
            Dictionary<string, CodeListType> dico = new Dictionary<string, CodeListType>();

            int defaultAlias = 0;
            foreach (CodelistRefType crt in this)
            {
                CodeListType clType = null;
                clType = ReferenceManager.LocateCodelist(crt);
                if (clType != null)
                {
                    // If alias not yet defined, propose a default
                    if (string.IsNullOrEmpty(crt.Alias))
                        crt.Alias = (++defaultAlias).ToString();
                    dico.Add(crt.Alias, clType);
                }
            }

            return dico;
        }
        #endregion

        #region String representation

        public override string ToString()
        {
            return "Codelist Ref.";
        }
        #endregion

    }
}
